<template>
  <div>
    <h1>大文件上传</h1>
    <input type="file" @change="handleUpload" />
  </div>
</template>
<script setup>
import SparkMD5 from "spark-md5";
import axios from "axios";
import { ref } from "vue";
const fileHash = ref("");
const fileName = ref("");
const CHUNK_SIZE = 1024 * 1024;
const createChunkk = (file) => {
  let cur = 0;
  let chunk = [];
  while (cur < file.size) {
    const blob = file.slice(cur, cur + CHUNK_SIZE);
    // 修复类型问题，明确指定数组元素类型为Blob
    chunk.push(blob);
    cur += CHUNK_SIZE;
  }
  return chunk;
};
const handleUpload = async (e) => {
  // 原代码中e的类型定义错误，应该是Event类型，而不是HTMLInputElement类型
  const target = e.target;
  const files = target.files;
  fileName.value = files[0].name;
  if (!files) return;
  const chunks = createChunkk(files[0]);
  const hash = await calculateHash(chunks);
  fileHash.value = hash;
  const res = await verify();
  console.log('res',res);
  
  if (res.data.shouldUpload) {
    uploadChunks(chunks,res.data.existChunks);
  } else {
    alert("文件已存在");
  }
};

const verify = async () => {
  const res = await fetch("http://localhost:3003/verify", {
    method: "POST",
    headers: {
      "content-type": "application/json",
    },
    body: JSON.stringify({
      fileHash: fileHash.value,
      fileName: fileName.value,
      size: CHUNK_SIZE,
    }),
  });
  const res1 = await res.json();
  return res1;
};

const calculateHash = (chunks) => {
  return new Promise((res) => {
    const targets = []; // 存储所有参加计算的切片
    const spark = new SparkMD5.ArrayBuffer();
    const fileReader = new FileReader();
    chunks.forEach((chunk, index) => {
      if (index === 0 || index === chunks.length - 1) {
        targets.push(chunk);
      } else {
        targets.push(chunk.slice(0, 2));
        targets.push(chunk.slice(CHUNK_SIZE / 2, CHUNK_SIZE / 2 + 2));
        targets.push(chunk.slice(CHUNK_SIZE - 2, CHUNK_SIZE));
      }
    });
    fileReader.readAsArrayBuffer(new Blob(targets));
    fileReader.onload = (e) => {
      spark.append(e.target.result);
      res(spark.end());
    };
  });
};
const uploadChunks = async (chunks,existChunks) => {
  const data = chunks.map((chunk, index) => {
    return {
      fileHash: fileHash.value,
      chunkHash: fileHash.value + "-" + index,
      chunk,
    };
  });

  const formDatas = data
  .filter((item) => !existChunks.includes(item.chunkHash))
  .map((item) => {
    const formData = new FormData();
    formData.append("fileHash", item.fileHash);
    formData.append("chunkHash", item.chunkHash);
    formData.append("chunk", item.chunk);
    return formData;
  });

  const max = 6;
  let index = 0;
  const taskPool = [];
  while (index < formDatas.length) {
    const task = axios("http://localhost:3003/upload", {
      method: "POST",
      data: formDatas[index],
    });
    taskPool.splice(taskPool.findIndex((item) => item === task));
    taskPool.push(task);
    if (taskPool.length === max) {
      await Promise.race(taskPool);
    }
    index++;
  }
  await Promise.all(taskPool);
  // 通知服务器合并文件
  mergeRequest();
};

const mergeRequest = async () => {
  const res = await fetch("http://localhost:3003/merge", {
    method: "POST",
    headers: {
      "content-type": "application/json",
    },
    body: JSON.stringify({
      fileHash: fileHash.value,
      fileName: fileName.value,
      size: CHUNK_SIZE,
    }),
  });
  alert("合并成功了！");
};
</script>
<style scoped></style>
